package com.eryos.android.cigarettecounter.io;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.util.Log;

import com.eryos.android.cigarettecounter.R;

/**
 * Project : CigaretteCounter Author : Olivier Combe URL : http://eryos.fr/CigaretteCounter/
 * 
 * Copyright(C)2011 - Olivier Combe
 * 
 * Licensed under the Apache License, Version 2.0(the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 * 
 * @version \$Revision.*\$
 * @author Olivier Combe
 */
public class LogReporter {

    private static final String LOG_TAG = "LogReporter";
    private static final String LINE_RETURN = "\n";
    private static final String LINE_SEPARATOR = "\n-------------------------------------\n";

    private String logFileName = "log-cigarettecounter.txt";

    private List<String> emailList;
    private List<String> emailListCc;
    private List<String> emailListBcc;
    private List<String> logFilterList;
    private String subject = "Log Trace";
    private String message = "Please find log trace file in attachment";
    private String type = "message/rfc822"; // text/plain";
    private Uri attachedFile;
    private int maxLineNumbers = 1000;
    private Context context;
    private String debugInfo = "";

    /**
     * Create a new log report.
     * 
     * @param ctx
     *            context to use
     */
    public LogReporter(final Context ctx) {
        Log.d(LOG_TAG, LOG_TAG);

        context = ctx;
        emailList = new ArrayList<String>();
        emailListCc = new ArrayList<String>();
        emailListBcc = new ArrayList<String>();
        logFilterList = new ArrayList<String>();

        Calendar c = Calendar.getInstance();
        String timestamp = c.get(Calendar.YEAR) + "" + c.get(Calendar.MONTH) + "" + c.get(Calendar.DAY_OF_MONTH) + ""
                + c.get(Calendar.HOUR_OF_DAY) + "" + c.get(Calendar.MINUTE) + "" + c.get(Calendar.SECOND);

        logFileName = "log_cigarettecounter_" + timestamp + ".txt";
    }

    /**
     * Method used to retrieve log and write trace on SD Card.
     */
    public void retrieveLogs() {
        Log.d(LOG_TAG, "retrieveLogs()");

        List<String> logs = generateLogTrace();

        /* Export data to external file */
        DataExporter export = new DataExporter(logFileName);

        try {
            if (export.openStream()) {

                Log.d(LOG_TAG, "write logs to SD Card File");
                for (String line : logs) {
                    export.writeDataToExternalFile(line + LINE_RETURN);
                }

                export.closeStream();
            }
        } catch (IOException e) {
            Log.e(LOG_TAG, "retrieveLogs()", e);
        }

        /* Create the Uri reference */
        attachedFile = Uri.parse("file://" + export.getFullPathName());
        Log.d(LOG_TAG, "Uri : " + "file://" + export.getFullPathName());
    }

    /**
     * Method used to retrieve logs from "logcat" command.
     * 
     * @return log content in a List
     */
    @SuppressWarnings("unchecked")
    private List<String> generateLogTrace() {
        Log.d(LOG_TAG, "generateLogTrace()");

        List<String> logs = new ArrayList<String>();

        try {
            /* Run logcat command */
            List<String> commandLine = new ArrayList<String>();
            commandLine.add("logcat");
            commandLine.add("-d");
            commandLine.add("-v");
            commandLine.add("time");

            if (logFilterList != null) {
                Log.d(LOG_TAG, "add FilterSpecs");
                for (String filterSpec : logFilterList) {
                    commandLine.add(filterSpec);
                }
            }

            String cmd = "";
            for (String arg : commandLine.toArray(new String[commandLine.size()])) {
                cmd = cmd + " " + arg;
            }

            Log.d(LOG_TAG, "run logcat cmd : " + cmd);
            Process process = Runtime.getRuntime().exec(commandLine.toArray(new String[commandLine.size()]));
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));

            /* Browse complete log trace */
            Log.d(LOG_TAG, "Browse complete logs");
            String line;
            while ((line = bufferedReader.readLine()) != null) {

                /* Apply defined filters to logs */
                if (logFilterList != null && logFilterList.size() > 0) {
                    for (String tag : logFilterList) {
                        if (line.toLowerCase().contains(tag.toLowerCase())) {
                            logs.add(line);
                            break;
                        }
                    }
                } else {
                    logs.add(line);
                }
            }
            Log.d(LOG_TAG, "Logs retrieve : " + logs.size());
        } catch (IOException e) {
            Log.e(LOG_TAG, "generateLogTrace", e);
        }

        /* Reduce the number of lines if needed */
        Log.d(LOG_TAG, "Resize logs if needed");
        if (logs.size() > maxLineNumbers) {
            int maxAuthorized = logs.size() - maxLineNumbers;
            Collection<String> listToRemove = ((ArrayList<String>) ((ArrayList<String>) logs).clone()).subList(0,
                    maxAuthorized); // use clone()to avoid ConcurrentModificationException
            logs.removeAll(listToRemove);
        }

        /* Return the final log list */
        return logs;
    }

    /**
     * Method used to retrieve additional info like location, timezone, SDK version, etc.
     */
    private void retrieveAdditionalInformation() {
        Log.d(LOG_TAG, "retrieveAdditionalInformation()");

        String appVersion = context.getString(R.string.version);
        String sdkVersion = Build.VERSION.SDK;
        String defaultCountry = Locale.getDefault().getDisplayCountry();
        String defaultLanguage = Locale.getDefault().getDisplayLanguage();
        String defaultTimezone = TimeZone.getDefault().getDisplayName();
        String screenResolution = Build.DISPLAY;

        String infos = "";
        infos = infos + "App Version      : " + appVersion + LINE_RETURN;
        infos = infos + "SDK Version      : " + sdkVersion + LINE_RETURN;
        infos = infos + "Default Country  : " + defaultCountry + LINE_RETURN;
        infos = infos + "Default Language : " + defaultLanguage + LINE_RETURN;
        infos = infos + "Default TimeZone : " + defaultTimezone + LINE_RETURN;
        infos = infos + "Display          : " + screenResolution + LINE_RETURN;

        if (message != null) {
            message = message + LINE_SEPARATOR + "Debug Information : " + LINE_RETURN + debugInfo + LINE_SEPARATOR
                    + "Additional Information : " + LINE_RETURN + infos;
        }
    }

    /**
     * Method used to raise an Intent for sending mail with pre-defined arguments.
     */
    public void sendLogs() {
        Log.d(LOG_TAG, "sendLogs()");

        /* Check if file is available on SD Card */
        if (true) {

            /* Add extra info to Message Body */
            retrieveAdditionalInformation();

            /* Define the ACTION_SEND Intent */
            Intent sendMailIntent = new Intent(Intent.ACTION_SEND);
            sendMailIntent.setType(type);
            sendMailIntent.putExtra(Intent.EXTRA_EMAIL, emailList.toArray(new String[emailList.size()]));
            sendMailIntent.putExtra(Intent.EXTRA_CC, emailListCc.toArray(new String[emailListCc.size()]));
            sendMailIntent.putExtra(Intent.EXTRA_BCC, emailListBcc.toArray(new String[emailListBcc.size()]));
            sendMailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
            sendMailIntent.putExtra(Intent.EXTRA_TEXT, message);
            sendMailIntent.putExtra(Intent.EXTRA_STREAM, attachedFile);

            /* Raise the new Intent */
            Log.d(LOG_TAG, "startActivity(mailIntent)");
            context.startActivity(Intent.createChooser(sendMailIntent, "Choose an appli to send the mail"));
        }
    }

    /**
     * @return the logFileName
     */
    public String getLogFileName() {
        return logFileName;
    }

    /**
     * @param logFileNameArg
     *            the logFileName to set
     */
    public void setLogFileName(final String logFileNameArg) {
        this.logFileName = logFileNameArg;
    }

    /**
     * @return the emailList
     */
    public List<String> getEmailList() {
        return emailList;
    }

    /**
     * @param emailListArg
     *            the emailList to set
     */
    public void setEmailList(final List<String> emailListArg) {
        this.emailList = emailListArg;
    }

    /**
     * @return the emailList_cc
     */
    public List<String> getEmailListCc() {
        return emailListCc;
    }

    /**
     * @param emailListCcArg
     *            the emailList_cc to set
     */
    public void setEmailListCc(final List<String> emailListCcArg) {
        this.emailListCc = emailList;
    }

    /**
     * @return the emailList_bcc
     */
    public List<String> getEmailListBcc() {
        return emailListBcc;
    }

    /**
     * @param emailListBccArg
     *            the emailList_bcc to set
     */
    public void setEmailListBcc(final List<String> emailListBccArg) {
        this.emailListBcc = emailListBccArg;
    }

    /**
     * @return the logFilterList
     */
    public List<String> getLogFilterList() {
        return logFilterList;
    }

    /**
     * @param logFilterListArg
     *            the logFilterList to set
     */
    public void setLogFilterList(final List<String> logFilterListArg) {
        this.logFilterList = logFilterListArg;
    }

    /**
     * @return the subject
     */
    public String getSubject() {
        return subject;
    }

    /**
     * @param subjectArg
     *            the subject to set
     */
    public void setSubject(final String subjectArg) {
        this.subject = subjectArg;
    }

    /**
     * @return the message
     */
    public String getMessage() {
        return message;
    }

    /**
     * @param messageArg
     *            the message to set
     */
    public void setMessage(final String messageArg) {
        this.message = messageArg;
    }

    /**
     * @return the type
     */
    public String getType() {
        return type;
    }

    /**
     * @param typeArg
     *            the type to set
     */
    public void setType(final String typeArg) {
        this.type = typeArg;
    }

    /**
     * @return the attachedFile
     */
    public Uri getAttachedFile() {
        return attachedFile;
    }

    /**
     * @param attachedFileArg
     *            the attachedFile to set
     */
    public void setAttachedFile(final Uri attachedFileArg) {
        this.attachedFile = attachedFileArg;
    }

    /**
     * @return the maxLineNumbers
     */
    public int getMaxLineNumbers() {
        return maxLineNumbers;
    }

    /**
     * @param maxLineNumbersArg
     *            the maxLineNumbers to set
     */
    public void setMaxLineNumbers(final int maxLineNumbersArg) {
        this.maxLineNumbers = maxLineNumbersArg;
    }

    /**
     * @return the context
     */
    public Context getContext() {
        return context;
    }

    /**
     * @param contextArg
     *            the context to set
     */
    public void setContext(final Context contextArg) {
        this.context = contextArg;
    }

    /**
     * @return the debugInfo
     */
    public String getDebugInfo() {
        return debugInfo;
    }

    /**
     * @param debugInfoArg
     *            the debugInfo to set
     */
    public void setDebugInfo(final String debugInfoArg) {
        this.debugInfo = debugInfoArg;
    }

}
